import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_widgets_pc/widgets/chart/kq_chart_model.dart';
import 'Kq_line_monotone_x.dart';
import 'kq_chart_line_model.dart';
import 'package:path_drawing/path_drawing.dart';

class KqChartLine extends StatefulWidget {
  ///内部参数设置
  final ChartLineModel chartModel;

  ///控住多图刷新的key，同一页面有多个图是必填
  final String? keyTag;

  ///背景色
  final Color? backGroundColor;

  ///总高度
  final double? height;

  ///总宽度
  final double? width;

  final List<List<ChartLineValue>>? valueList;

  const KqChartLine(
      {Key? key,
      required this.chartModel,
      this.keyTag,
      this.height,
      this.width,
      this.valueList,
      this.backGroundColor})
      : super(key: key);

  @override
  State<KqChartLine> createState() => _KqChartLineState();
}

class _KqChartLineState extends State<KqChartLine>
    with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;
  late ChartLineModel lineModel;

  @override
  void initState() {
    super.initState();
    lineModel = Get.put(widget.chartModel, tag: widget.keyTag);
    lineModel.setValueList(widget.valueList ?? widget.chartModel.chartArry);
    controller = AnimationController(
      duration: lineModel.chartModel.duration,
      vsync: this,
    );
    animation = Tween(begin: 0.0, end: lineModel.xWidth).animate(controller)
      ..addListener(() {
        lineModel.chartModel.animationX = animation.value;
        lineModel.update();
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          lineModel.chartModel.isAnimationEnd = true;
          lineModel.update();
        }
      });
    //启动动画(正向执行)
  }

  @override
  dispose() {
    //路由销毁时需要释放动画资源
    controller.dispose();
    super.dispose();
  }

  KqLineChartPainter getLineChart() {
    KqChartModel model = lineModel.chartModel;
    KqLineChartPainter painter = KqLineChartPainter(
        yStyle: TextStyle(fontSize: model.yFont, color: model.yColor),
        xStyle: TextStyle(fontSize: model.xFont, color: model.xColor),
        crossShowList: model.crossShowList ?? [],
        maxY: lineModel.maxHeight,
        lines: lineModel.chartArry,
        xList: lineModel.xList,
        xValue: lineModel.xValue,
        xRinght: model.xRight,
        yList: lineModel.yList,
        yValue: lineModel.yValue,
        isDivide: model.isDivide,
        axisColor: model.axisColor!,
        dividerColor: model.dividerColor!,
        isCurve: model.isCurve,
        animationX: model.animationX,
        isShowPoint: model.isShowPoint,
        pointRadius: model.pointRadius,
        isShowValue: model.isShowValue,
        hintLineSolid: model.hintLineSolid,
        isAddMask: model.isAddMask,
        isAnimationEnd: lineModel.chartModel.isAnimationEnd);
    return painter;
  }

  @override
  Widget build(BuildContext context) {
    lineModel.chartModel.isAnimationEnd = false;
    controller.forward(from: 0);
    lineModel.setValueList(widget.valueList ?? widget.chartModel.chartArry);
    KqChartModel item = lineModel.chartModel;
    return Container(
      width: widget.width,
      height: widget.height,
      color: widget.backGroundColor,
      child: RepaintBoundary(
          child: Stack(
        children: [
          GetBuilder<ChartLineModel>(
            init: lineModel,
            tag: widget.keyTag,
            builder: (controller) => CustomPaint(
              size: Size(item.sizeWidth, item.sizeHeight),
              foregroundPainter:
                  widget.backGroundColor != null ? getLineChart() : null,
              painter: widget.backGroundColor == null ? getLineChart() : null,
              child: widget.backGroundColor != null
                  ? Container(
                      alignment: Alignment.center,
                      width: item.sizeWidth,
                      height: item.sizeWidth,
                    )
                  : null,
            ),
          ),
          Padding(
            padding: EdgeInsets.only(
                left: item.xLeft,
                top: item.yTop,
                right: item.xRight,
                bottom: item.yBottom),
            child: SizedBox(
              width: lineModel.xWidth,
              height: lineModel.yHeight,
              child: GestureDetector(
                onPanDown: (details) {
                  if (!item.isShowValue) {
                    if (item.isShowClickValue) {
                      for (List<ChartLineValue> list in lineModel.chartArry) {
                        for (ChartLineValue info in list) {
                          double x = info.xPoint - item.xLeft;
                          double y = info.yPoint - item.yTop;
                          if ((x - 10 < details.localPosition.dx &&
                                  x + 10 > details.localPosition.dx) &&
                              (y - 10 < details.localPosition.dy &&
                                  y + 10 > details.localPosition.dy)) {
                            info.isShow = true;
                          } else {
                            info.isShow = false;
                          }
                        }
                      }
                    }
                    lineModel.update();
                  }
                },
                onPanUpdate: (details) {
                  if (!item.isShowValue) {
                    if (item.isShowSlideValue) {
                      for (List<ChartLineValue> list in lineModel.chartArry) {
                        for (ChartLineValue info in list) {
                          double x = info.xPoint - item.xLeft;
                          if (x - 10 < details.localPosition.dx &&
                              x + 10 > details.localPosition.dx) {
                            info.isShow = true;
                          } else {
                            info.isShow = false;
                          }
                        }
                      }
                    }
                    lineModel.update();
                  }
                },
                onPanEnd: (DragEndDetails e) {
                  // if (!lineModel.isShowValue) {
                  //   lineModel.update();
                  // }
                },
              ),
            ),
          )
          // GestureDetector(),
        ],
      )),
    );
  }
}

//
class KqLineChartPainter extends CustomPainter {
  ///动画是否结束
  final bool isAnimationEnd;

  ///轴线颜色
  final Color axisColor;

  ///分割线颜色
  final Color dividerColor;

  ///分割线是否为实现
  final bool hintLineSolid;

  ///是否展示分割线,默认为true
  final bool isDivide;

  ///是否曲线
  final bool isCurve;

  ///Y轴文案样式
  final TextStyle yStyle;

  ///X轴文案样式
  final TextStyle xStyle;

  ///x轴坐标数组
  final List xList;

  ///y轴坐标数组
  final List yList;

  ///横坐标内容
  List xValue;

  ///纵坐标内容
  List yValue;

  ///x轴距离左边距离;
  final double xRinght;

  ///最大值
  final double maxY;

  ///是否显示内容坐标点
  bool isShowPoint;

  ///是否显示全部的内容文案
  bool isShowValue;

  ///内容坐标点的半径
  double pointRadius;

  ///是否显示遮罩层
  bool isAddMask;

  ///具体数据
  final List<List<ChartLineValue>> lines;

  ///动画进行到X坐标
  final double animationX;

  ///横坐标内容显示对应下标
  List<int> crossShowList;

  List<LineCanvasModel> lineModels = [];

  KqLineChartPainter({
    Key? key,
    required this.crossShowList,
    required this.isAnimationEnd,
    required this.yStyle,
    required this.xStyle,
    required this.isCurve,
    required this.maxY,
    required this.axisColor,
    required this.dividerColor,
    required this.isDivide,
    required this.xList,
    required this.yList,
    required this.xValue,
    required this.yValue,
    required this.xRinght,
    required this.animationX,
    required this.lines,
    required this.isShowPoint,
    required this.pointRadius,
    required this.isShowValue,
    required this.hintLineSolid,
    required this.isAddMask,
  }) : super();

  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()
      ..strokeWidth = 1
      ..style = PaintingStyle.stroke;
    _drawXYAxisPath(canvas, paint);
    _initPath(canvas, paint);
  }

  //计算path
  void _initPath(Canvas canvas, Paint xyPaint) {
    for (int i = 0; i < lines.length; i++) {
      List<ChartLineValue> points = lines[i];
      Path path = Path();
      if (points.isNotEmpty) {
        if (isCurve) {
          path = _getSmoothLinePath(points);
        } else {
          path.moveTo(points[0].xPoint, points[0].yPoint);
          for (int j = 0; j < points.length; j++) {
            ChartLineValue point = points[j];
            path.lineTo(point.xPoint, point.yPoint);
          }
        }
        List paths = getAnimationPath(path, points);
        Path shadowPath = paths[1];
        lineModels.add(LineCanvasModel(
          path: paths.first,
          points: paths.last,
          shadowPaths: shadowPath,
          lineCorlor: points.first.lineColor,
          lineWidth: points.first.lineWidth,
          shaderColors: points.first.shaderColors,
        ));
      }
    }
    _drawLine(canvas);
  }

  //计算运动路径
  List getAnimationPath(Path path, List<ChartLineValue> points) {
    PathMetrics ma = path.computeMetrics();
    Path newPath = Path();
    Path shadowPath = Path();
    int pointIndex = 0;
    Tangent? lastPinnt;
    while (ma.iterator.moveNext()) {
      PathMetric mm = ma.iterator.current;
      newPath.addPath(
          mm.extractPath(
              0, animationX / (xList.last - xList.first) * mm.length),
          Offset.zero);
      shadowPath.addPath(
          mm.extractPath(
              0, animationX / (xList.last - xList.first) * mm.length),
          Offset.zero);
      lastPinnt = mm.getTangentForOffset(
          animationX / (xList.last - xList.first) * mm.length);
      pointIndex = (animationX / (xList[1] - xList.first)).ceil();
      break;
    }
    pointIndex = isAnimationEnd ? xList.length : pointIndex;
    shadowPath.lineTo(lastPinnt?.position.dx ?? xList.last, yList.last);
    shadowPath.lineTo(xList.first, yList.last);
    shadowPath.close();
    return [newPath, shadowPath, pointIndex];
  }

  ///计算曲线坐标
  Path _getSmoothLinePath(List<ChartLineValue> values) {
    List<Point> points = [];
    values.map((ChartLineValue e) {
      points.add(Point(e.xPoint, e.yPoint));
    }).toList();
    var targetPoints = <Point>[];
    targetPoints.addAll(points);
    targetPoints.add(Point(
        points[points.length - 1].x * 2, points[points.length - 1].y * 2));
    double? x0, y0, x1, y1, t0;
    var path = Path();
    for (int i = 0; i < targetPoints.length; i++) {
      double? t1;
      var x = targetPoints[i].x;
      var y = targetPoints[i].y;
      if (x == x1 && y == y1) break;
      switch (i) {
        case 0:
          path.moveTo(x as double, y as double);
          break;
        case 1:
          break;
        case 2:
          t1 = MonotoneX.slope3(x0!, y0!, x1!, y1!, x as double, y as double);
          MonotoneX.point(
              path, x0, y0, x1, y1, MonotoneX.slope2(x0, y0, x1, y1, t1), t1);
          break;
        default:
          t1 = MonotoneX.slope3(x0!, y0!, x1!, y1!, x as double, y as double);
          MonotoneX.point(path, x0, y0, x1, y1, t0!, t1);
      }
      x0 = x1;
      y0 = y1;
      x1 = x as double;
      y1 = y as double;
      t0 = t1;
    }
    return path;
  }

  ///绘制曲线或者折线以及文案
  void _drawLine(Canvas canvas) {
    if (lineModels.isNotEmpty) {
      for (int i = 0; i < lineModels.length; i++) {
        LineCanvasModel model = lineModels[i];
        if (isAddMask) {
          Rect shadowRect =
              Rect.fromLTRB(xList.first, yList.first, xList.last, yList.last);
          var shader = LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  tileMode: TileMode.clamp,
                  colors: model.shaderColors)
              .createShader(shadowRect);
          // canvas.save();
          // canvas.clipPath(path)
          // canvas.drawImage(image, offset, paint)
          // canvas.restore()
          canvas.drawPath(
              model.shadowPaths,
              Paint()
                ..shader = shader
                ..isAntiAlias = true
                ..style = PaintingStyle.fill);
        }
        canvas.drawPath(
            model.path,
            Paint()
              ..strokeWidth = model.lineWidth
              ..color = model.lineCorlor
              ..style = PaintingStyle.stroke);
        List<ChartLineValue> info = lines[i];
        for (int n = 0; n < model.points; n++) {
          ChartLineValue value = info[n];
          if (isShowPoint) {
            canvas.drawCircle(
                Offset(value.xPoint, value.yPoint),
                pointRadius,
                Paint()
                  ..isAntiAlias = true
                  ..strokeCap = StrokeCap.round
                  ..color = model.lineCorlor
                  ..strokeWidth = 1
                  ..style = PaintingStyle.fill);
          }
          if (isShowValue) {
            var textValue = TextPainter(
                textAlign: TextAlign.center,
                ellipsis: '.',
                maxLines: 1,
                text: TextSpan(text: value.yValue, style: value.style),
                textDirection: TextDirection.rtl)
              ..layout();
            textValue.paint(
                canvas,
                Offset(value.xPoint - textValue.width / 2,
                    value.yPoint - textValue.height - 6));
          } else {
            if (value.isShow) {
              var textValue = TextPainter(
                  textAlign: TextAlign.center,
                  ellipsis: '.',
                  maxLines: 1,
                  text: TextSpan(text: value.yValue, style: value.style),
                  textDirection: TextDirection.rtl)
                ..layout();
              textValue.paint(
                  canvas,
                  Offset(value.xPoint - textValue.width / 2,
                      value.yPoint - textValue.height - 6));
              if (info.first.isCountDivider) {
                var paint = Paint()
                  ..strokeWidth = 1
                  ..style = PaintingStyle.stroke
                  ..color = info.first.dividerColor;
                Path horizontalPath = Path();
                horizontalPath.moveTo(value.xPoint, yList.last);
                horizontalPath.lineTo(value.xPoint, value.yPoint);
                if (hintLineSolid) {
                  canvas.drawPath(horizontalPath, paint);
                } else {
                  canvas.drawPath(
                    dashPath(
                      horizontalPath,
                      dashArray:
                          CircularIntervalList<double>(<double>[4.0, 2.0]),
                    ),
                    paint,
                  );
                }

                Path verticalPath = Path();
                verticalPath.moveTo(xList.first, value.yPoint);
                verticalPath.lineTo(value.xPoint, value.yPoint);
                if (hintLineSolid) {
                  canvas.drawPath(verticalPath, paint);
                } else {
                  canvas.drawPath(
                    dashPath(
                      verticalPath,
                      dashArray:
                          CircularIntervalList<double>(<double>[4.0, 2.0]),
                    ),
                    paint,
                  );
                }
              }
            }
          }
        }
      }
    }
  }

  //绘制X轴Y轴，Y轴分割线
  void _drawXYAxisPath(Canvas canvas, Paint paint) {
    paint.color = axisColor;
    Path horizontalPath = Path();
    horizontalPath.moveTo(xList.first, yList.last);
    horizontalPath.lineTo(xList.last + xRinght, yList.last);
    canvas.drawPath(horizontalPath, paint);

    Path verticalPath = Path();
    verticalPath.moveTo(xList.first, 0);
    verticalPath.lineTo(xList.first, yList.last);
    canvas.drawPath(verticalPath, paint);
    _drawYValue(canvas, paint);
    _drawXValye(canvas, paint);

    if (isDivide == true) {
      paint.color = dividerColor;
      for (int i = 0; i < yList.length - 1; i++) {
        Path dividerPath = Path();
        dividerPath.moveTo(xList.first, yList[i]);
        dividerPath.lineTo(xList.last + xRinght, yList[i]);
        if (hintLineSolid) {
          canvas.drawPath(dividerPath, paint);
        } else {
          canvas.drawPath(
            dashPath(
              dividerPath,
              dashArray: CircularIntervalList<double>(<double>[4.0, 2.0]),
            ),
            paint,
          );
        }
      }
    }
  }

  /// 绘制y轴文本
  void _drawYValue(Canvas canvas, Paint paint) {
    for (int i = 0; i < yValue.length; i++) {
      var textY = TextPainter(
          textAlign: TextAlign.right,
          ellipsis: '.',
          maxLines: 1,
          text: TextSpan(text: yValue[i], style: yStyle),
          textDirection: TextDirection.rtl)
        ..layout();
      textY.paint(canvas,
          Offset(xList.first - textY.width - 6, yList[i] - textY.height / 2));
    }
  }

  /// 绘制x轴文本
  void _drawXValye(Canvas canvas, Paint paint) {
    if (crossShowList.isNotEmpty) {
      for (int i = 0; i < crossShowList.length; i++) {
        int index = crossShowList[i];
        var textX = TextPainter(
            textAlign: TextAlign.center,
            ellipsis: '.',
            maxLines: 1,
            text: TextSpan(text: xValue[index], style: xStyle),
            textDirection: TextDirection.rtl)
          ..layout();
        textX.paint(
            canvas, Offset(xList[index] - textX.width / 2, yList.last + 6));
      }
    } else {
      for (int i = 0; i < xList.length; i++) {
        var textX = TextPainter(
            textAlign: TextAlign.center,
            ellipsis: '.',
            maxLines: 1,
            text: TextSpan(text: xValue[i], style: xStyle),
            textDirection: TextDirection.rtl)
          ..layout();
        textX.paint(canvas, Offset(xList[i] - textX.width / 2, yList.last + 6));
      }
    }
  }

  @override
  bool shouldRepaint(covariant KqLineChartPainter oldDelegate) {
    return true;
  }
}

//绘制图表的计算之后的结果模型集
class LineCanvasModel {
  ///遮罩层路径
  Path shadowPaths;

  ///折线或曲线路径
  Path path;

  ///点的数组
  // List<Offset> points;
  int points;

  ///遮罩层颜色
  List<Color> shaderColors;

  ///线的颜色
  Color lineCorlor;

  double lineWidth;

  LineCanvasModel({
    required this.points,
    required this.shadowPaths,
    required this.shaderColors,
    required this.lineCorlor,
    required this.path,
    required this.lineWidth,
  });
}
